﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

namespace GPSiTracProtocol
{
    public class GPSiTracProtocol900C
    {
        public enum GPSDeviceCommandWord
        {
            LocationData = 0x80,
            Handshake = 0xB1,
            CallRollData = 0x81,
            Received = 0x85,
            LinkCenter = 0xB0
        }

        public enum GPSTrackingCenterCommandWord
        {
            CallTheRoll = 0x30,
            IntervalReporting = 0x34,
            CancelAlarm = 0x37,
            ControlResumingOil = 0x38,
            ControlCuttingOil = 0x39,
            OverSpeed = 0x3F,
            OK = 0x21

        }

        public enum MessageImei
        {
            Imei = 5
        }

        public enum MessageDateTime
        {
            Year = 9,
            Month = 10,
            Day = 11,
            Hour = 12,
            Minute = 13,
            Second = 14
        };

        public enum MessageCoordinate
        {
            Latitude = 15,
            Longitude = 19
        };

        public enum MessageSpeed
        {
            Speed = 23
        }

        public enum MessageHeading
        {
            Heading = 25
        }

        public enum MessageEvent
        {
		    GPSAvailable   = 27,
            ACC            = 31,
            InputShock     = 31,
            InputDoor      = 31,
            MainPower      = 31,
            OilCutting     = 31,
            SosAlarm       = 32,
            OverSpeed      = 32,
            GeoferenceEnterAlarm = 32,
            GeoferenceExitAlarm = 32,
            CircuitCutting = 34
            
            		
        }

        public enum MessageAlarm 
        {
            GeoferenceEnterAlarm = 1,
            GeoferenceExitAlarm = 2,
            OverspeedAlarm = 3,
            SosAlarm       = 4,
            MainPowerCut = 5

        }

        static byte[] PackageHeader = new byte[] { 0x29, 0x29 };
        static byte[] PackageTrailer = new byte[] { 0x0D };
        static int MessageBufferSize = 512;

        /// <summary>
        /// Initializes the property GPS Device ID;
        /// </summary>
        static byte[] _GPSDeviceID;

        /// <summary>
        /// Initializes the property Message Device;
        /// </summary>
        static byte[] _MessageDevice;


        public static byte[] MessageDevice
        {
            set
            {
                _MessageDevice = value;
            }
        }

        public static byte[] GPSDeviceID
        {
            set
            {
                _GPSDeviceID = value;
            }
        }


        public static string GetImei()
        {
            return BitConverter.ToInt32(_MessageDevice, (int)MessageImei.Imei).ToString();
        }

        public static DateTime GetDateTime()
        {
            int Year = ConvertBCDToInt(_MessageDevice[(int)MessageDateTime.Year]);
            int Month = ConvertBCDToInt(_MessageDevice[(int)MessageDateTime.Month]);
            int Day = ConvertBCDToInt(_MessageDevice[(int)MessageDateTime.Day]);

            int Hour = ConvertBCDToInt(_MessageDevice[(int)MessageDateTime.Hour]);
            int Minute = ConvertBCDToInt(_MessageDevice[(int)MessageDateTime.Minute]);
            int Second = ConvertBCDToInt(_MessageDevice[(int)MessageDateTime.Second]);

            return new DateTime(Year, Month, Day, Hour, Minute, Second);
        }

        public static double GetCoordinate(MessageCoordinate enumMessageCoordinate)
        {
            int index = (int)MessageCoordinate.Longitude;
            if (enumMessageCoordinate == MessageCoordinate.Latitude)
                index = (int)MessageCoordinate.Latitude;


            bool IsSouthHemisphere = (_MessageDevice[index] & 128) != 0;

            string Hemisphere = "N";
            if (IsSouthHemisphere)
                Hemisphere = "S";

            string LatitudeFormat = string.Format("{0:X2}", _MessageDevice[index] & 127) +
                                    string.Format("{0:X2}", _MessageDevice[index + 1]);

            int Latitude = ConvertBCDToInt(_MessageDevice[index + 2]);
            LatitudeFormat += Convert.ToString(Latitude / 10) + "." + Convert.ToString(Latitude % 10) + string.Format("{0:X2}", _MessageDevice[index + 3]);


            double LatitudeNumber = double.Parse(LatitudeFormat, NumberStyles.Float, NumberFormatInfo.InvariantInfo);

            int LatitudeValue2 = (int)LatitudeNumber;
            int LatitudeValue3 = LatitudeValue2 % 100;
            int LatitudeValue4 = (LatitudeValue2 - LatitudeValue3) / 100;
            LatitudeNumber = (double)LatitudeValue4 + ((double)LatitudeValue3 + LatitudeNumber - (double)LatitudeValue2) / 60;

            if (Hemisphere == "W" || Hemisphere == "S")
                LatitudeNumber = -LatitudeNumber;

            return LatitudeNumber;

        }

        public static double GetSpeed()
        {
            return Convert.ToDouble(ConvertBCDToInt(_MessageDevice[(int)MessageSpeed.Speed]) * 100 + ConvertBCDToInt(_MessageDevice[(int)MessageSpeed.Speed + 1]));
        }

        public static double GetHeading()
        {
            return Convert.ToDouble(ConvertBCDToInt(_MessageDevice[(int)MessageHeading.Heading]) * 100 + ConvertBCDToInt(_MessageDevice[(int)MessageHeading.Heading + 1]));
        }
				
		public static bool IsGPSAvailable()
        {
            return (_MessageDevice[(int)MessageEvent.GPSAvailable] & 128) != 0;
        }

        public static bool IsACCOn()
        {
            return (_MessageDevice[(int)MessageEvent.ACC] & 128) == 0;
        }

        public static bool IsInputShockActivated()
        {
            return (_MessageDevice[(int)MessageEvent.InputShock] & 64) != 0;
        }

        public static bool IsInputDoorOpen()
        {
            return (_MessageDevice[(int)MessageEvent.InputDoor] & 32) != 0;
        }

        public static bool IsMainPowerOn()
        {
            return (_MessageDevice[(int)MessageEvent.MainPower] & 128) == 0;
        }

        public static bool IsOilCuttingActivated()
        {
            return (_MessageDevice[(int)MessageEvent.OilCutting] & 4) != 0;
        }        

        public static bool IsEmergencySOSAlarmActivated()
        {
            return (_MessageDevice[(int)MessageEvent.SosAlarm] & 128) == 0;
        }

        public static bool IsOverSpeedAlarmActivated()
        {
            return (_MessageDevice[(int)MessageEvent.OverSpeed] & 64) == 0;
        }

        public static bool IsGeoferenceExitAlarmActivated()
        {
            return (_MessageDevice[(int)MessageEvent.GeoferenceExitAlarm] & 16) == 0;
        }

        public static bool IsGeoferenceEnterAlarmActivated()
        {
            return (_MessageDevice[(int)MessageEvent.GeoferenceEnterAlarm] & 8) == 0;
        }

        public static bool IsCircuitCuttingActivated()
        {
            return (_MessageDevice[(int)MessageEvent.CircuitCutting] & 8) != 0;
        }

        public static byte[] SendCallTheRoll()
        {
            return BuildMessageGPSTrackingCenter(GPSTrackingCenterCommandWord.CallTheRoll,_GPSDeviceID, new byte[]{}); 
        }

        public static byte[] SendSetTimeIntervalReporting( byte[] _GPSDeviceID, int Hour, int Min, int Seg)
        {            
            TimeSpan Interval = new TimeSpan(Hour, Min, Seg);
            byte[] IntervalByte = new byte[1]{Convert.ToByte(Interval.TotalSeconds)};
            return BuildMessageGPSTrackingCenter(GPSTrackingCenterCommandWord.CallTheRoll, _GPSDeviceID, IntervalByte); 
        }
		
		public static byte[] CancelAlarm()
        {            
            return BuildMessageGPSTrackingCenter(GPSTrackingCenterCommandWord.CancelAlarm, _GPSDeviceID, new byte[]{}); 
        }
		
		public static byte[] ControlResumingOil()
        {            
            return BuildMessageGPSTrackingCenter(GPSTrackingCenterCommandWord.ControlResumingOil, _GPSDeviceID, new byte[]{}); 
        }
		
		public static byte[] ControlCuttingOil()
        {            
            return BuildMessageGPSTrackingCenter(GPSTrackingCenterCommandWord.ControlCuttingOil, _GPSDeviceID, new byte[]{}); 
        }
		
		public static byte[] SetOverSpeedValue( byte Speed)
        {            
            return BuildMessageGPSTrackingCenter(GPSTrackingCenterCommandWord.OverSpeed, _GPSDeviceID, new byte[]{ Speed }); 
        }

        public static byte[] SendOK()
        {           
            return BuildMessageGPSTrackingCenter(GPSTrackingCenterCommandWord.OK, _GPSDeviceID, new byte[] { _MessageDevice[_MessageDevice.Length - 2], 0x80, 0x80 }); 
        }

        public static byte[] GetGPSID()
        {
            return _MessageDevice.Skip(5).Take(4).ToArray();
        }

        public static byte[] BuildMessageGPSTrackingCenter(GPSTrackingCenterCommandWord enumGPSTrackingCenterCommandWord, byte[] _GPSDeviceID, byte[] Parameters)
        {
            byte[] byteData = new byte[0];     
            // Header
            byteData = byteData.Concat<byte>(PackageHeader).ToArray();
            // Command Word
            byteData = byteData.Concat<byte>(new byte[] { (byte)enumGPSTrackingCenterCommandWord }).ToArray();
            // Package Length
            switch (enumGPSTrackingCenterCommandWord)
            {
                case GPSTrackingCenterCommandWord.ControlCuttingOil:
                case GPSTrackingCenterCommandWord.ControlResumingOil:
                case GPSTrackingCenterCommandWord.CancelAlarm:
                case GPSTrackingCenterCommandWord.CallTheRoll:
                byteData = byteData.Concat<byte>(new byte[] { 0x00, 0x06 }).ToArray();
                break;

                case GPSTrackingCenterCommandWord.IntervalReporting:
                byteData = byteData.Concat<byte>(new byte[] { 0x00, 0x08 }).ToArray();
                break;

                case GPSTrackingCenterCommandWord.OverSpeed:
                byteData = byteData.Concat<byte>(new byte[] { 0x00, 0x07 }).ToArray();
                break;

                case GPSTrackingCenterCommandWord.OK:
                byteData = byteData.Concat<byte>(new byte[] { 0x00, 0x05 }).ToArray();
                break;                                 

                default:
                break;
            }
            //GPS Device ID
            byteData = byteData.Concat<byte>(_GPSDeviceID).ToArray();
            //Parameters
            if (Parameters.Length > 0) byteData = byteData.Concat<byte>(Parameters).ToArray();  
            // Check Code
            byteData = byteData.Concat<byte>(new byte[]{CalcCheckCode(byteData)}).ToArray();
            //Trailer
            byteData = byteData.Concat<byte>(PackageTrailer).ToArray();

            return byteData;
        }               
        
        public static int ConvertBCDToInt(byte Data)
        {
            int returnData = (Data & 15) + (Data >> 4 & 15) * 10;
            return returnData;
        }

        public static byte CalcCheckCode(byte[] byteData)
        {
            byte num = 0;
            int num1 = 2;
            while (true)
            {
                bool length = num1 < (int)_MessageDevice.Length - 2;
                if (!length)
                {
                    break;
                }
                num = (byte)(num ^ _MessageDevice[num1]);
                num1++;
            }
            byte num2 = num;
            return num2;
        }

        public static string ConvertToHexString(byte[] MessageByte, int bytesRead)
        {
            if (bytesRead == -1)
                MessageByte = MessageByte.Reverse().SkipWhile(x => x == 0).Reverse().ToArray();
            else
                Array.Resize<byte>(ref MessageByte, bytesRead);

            StringBuilder sb = new StringBuilder();
            foreach (byte b in MessageByte)
            {
                sb.AppendFormat("0x{0:X2},", b);
            }
            return sb.ToString();
        }

        public static byte[] ConvertStringHexToByte(string StringHex)
        {
            string[] _str = StringHex.Trim().Replace("0x", "").Split(',');

            byte[] _Byte = _str.Select(s => Byte.Parse(s,
                                       NumberStyles.HexNumber,
                                       CultureInfo.InvariantCulture)).ToArray();

            return _Byte;
        }
    }
}
